# This file is part of OrangeSpider. OrangeSpider is a meta model based
# development tool.
# 
# OrangeSpider is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# OrangeSpider is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with OrangeSpider.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright 2009 Stefan Nikolaus <OrangeSpiderMaster@googlemail.com>

"""
Define an abstract interface for using components
Components: Application
          Pool -> Repositories
                  Components
                 RepositoryTypes

++ Connectoren zu allen Tools, inkl. PlugIns in C++, C#, OLE, Basic
  Eclipse, Visual Studio, ArgoUML

Application Repository: Menus, Toolbars
Project Repository
e.g. UML Editor: PlugIn Concept?
"""
from MetaModel import *
import ConfigParser
import os

class Component:
  def __init__(self):
     pass
                     
  def interface(self, interfaceName):
     return None

  # This method is used to setup component types 
  @staticmethod
  def _setupComponentType(rSpiderCOMP, name, desc, hasUi, interfaceList):
        ct = rSpiderCOMP.find("ComponentType", "Name", name)
        if not ct:
            ct = rSpiderCOMP.Class("ComponentType")
            ct["Description"] = desc
            ct["Name"] = name
            ct["HasUi"] = hasUi
            for ifcName in interfaceList:
                ifc = rSpiderCOMP.find("Interface", "Name", ifcName)
                assert(ifc)
                rSpiderCOMP.Assoc(ct, ifc, "Supports")
        return ct

class DirectoryImporter(Component):
  def __init__(self):
      Component.__init__(self)
      r = buildMetaModel("Directory", "SpiderFILE")
      self.repository = r

  def interface(self, interfaceName):
    if interfaceName == "Spider.IManagedComponent":
      return self

  # Interface Spider.IManagedComponent
  @staticmethod
  def setupComponentType(rSpiderCOMP):
    return Component._setupComponentType(rSpiderCOMP , "Spider.DirectoryImporter",
                                         "Directory Importer",
                                         0, ["Spider.IManagedComponent"])
     
  def read(self, top, filter):
     r = self.repository
     iter = os.walk(top)
     self._walk(iter, None)
     
  def _walk(self, iter, fileParent):
     r = self.repository
     try:
         current = iter.next()
         fileFolder = r.Class("Folder")
         if fileParent is None:
             fileFolder["Name"] = current[0]
         else:
             fileFolder["Name"] = os.path.basename(current[0])
             r.Assoc(fileParent, fileFolder, "Contains")
         for childFolderName in current[1]:
             self._walk(iter, fileFolder)
         for childFileName in current[2]:
             fileFile = r.Class("File")
             fileFile["Name"] = childFileName
             r.Assoc(fileFolder, fileFile, "Contains")
     finally:
        pass
        
class IniFileReader(Component):
  def __init__(self):
     Component.__init__(self)
     r = buildMetaModel("Ini rep", "SpiderINI")
     self.repository = r

  def interface(self, interfaceName):
    if interfaceName == "Spider.IManagedComponent":
      return self

  # Interface Spider.IManagedComponent
  @staticmethod
  def setupComponentType(rSpiderCOMP):
    return Component._setupComponentType(rSpiderCOMP , "Spider.IniFileReader",
                                         "Ini File reader",
                                         0, ["Spider.IManagedComponent"])
     

  def read(self, filename):
     cp = ConfigParser.SafeConfigParser()
     cp.read(filename)
     r = self.repository
     iniFile = r.Class("IniFile")
     iniFile["Name"] = filename
     for sectionName in cp.sections():
        iniSection = r.Class("Section")
        iniSection["Name"] = sectionName
        r.Assoc(iniFile, iniSection, "Contains")
        for key, value in cp.items(sectionName):
             iniSetting = r.Class("Setting")
             iniSetting["Name"] = key
             iniSetting["Value"] = value
             r.Assoc(iniSection, iniSetting, "Has")
     
  def write(self, filename):
     cp = ConfigParser.SafeConfigParser()
     r = self.repository
     for iniFile in r.eachClass("IniFile"):
        for iniSection in iniFile.eachOutgoingClass("IniFile.Contains.Section"):
            cp.add_section(iniSection["Name"])
            for iniSetting in iniSection.eachOutgoingClass("Section.Has.Setting"):
                cp.set(iniSection["Name"], iniSetting["Name"], iniSetting["Value"])
        f = open(filename, "w")
        cp.write(f)


class MetaModelConverter(Component):
    def __init__(self):
        Component.__init__(self)

    def interface(self, interfaceName):
        if interfaceName == "Spider.IManagedComponent":
            return self

    # Interface Spider.IManagedComponent
    @staticmethod
    def setupComponentType(rSpiderCOMP):
        return Component._setupComponentType(rSpiderCOMP , "Spider.MetaModelConverter",
                                             "Meta Model Converter",
                                             0, ["Spider.IManagedComponent"])
     
    def _createMetaModel(self, watcher = None):
        r = buildMetaModel("MetaModel", "SpiderMMM", watcher)
        return r

    def instantiiate(self, rMMM):
        mmmMetaModel = getFirst(rMMM.metaClasses["MetaModel"].extension)
        if mmmMetaModel == None:
            return None
        r = MetaModel("Instantiated " + mmmMetaModel["Name"], 
                      mmmMetaModel["Type"])
        
        # First: create types and enums
        for mmmMetaType in mmmMetaModel.eachOutgoingClass("MetaModel.Contains.MetaType"):
            mmmMetaEnum = mmmMetaType.firstOutgoingClass("MetaType.Is.MetaEnum")
            if mmmMetaEnum == None:
                r.MetaType(mmmMetaType["Name"], 
                           mmmMetaType["DefaultValue"], 
                           mmmMetaType["Description"])
            else:
                r.MetaEnum(mmmMetaType["Name"], 
                           mmmMetaType["Description"])
                for mmmMetaAtom in mmmMetaEnum.eachOutgoingClass("MetaEnum.HasPart.MetaAtom"):
                    r.MetaAtom(mmmMetaType["Name"], 
                               mmmMetaAtom["Name"], 
                               mmmMetaAtom["Description"])

        # Second: create metaclasses
        for mmmMetaClass in mmmMetaModel.eachOutgoingClass("MetaModel.Contains.MetaClass"):
            r.MetaClass(mmmMetaClass["Name"], mmmMetaClass["Description"])
            self._instantiiateMetaProps(r, mmmMetaClass)

        # Third: create metaassocs
        for mmmMetaAssoc in mmmMetaModel.eachOutgoingClass("MetaModel.Contains.MetaAssoc"):
            r.MetaAssoc(mmmMetaAssoc["Name"], 
                        mmmMetaAssoc["Description"], 
                        mmmMetaAssoc["SourceCardinality"] + ":" + mmmMetaAssoc["TargetCardinality"], 
                        mmmMetaAssoc["Aggregation"])
            self._instantiiateMetaProps(r, mmmMetaAssoc)

        return r

    def _instantiiateMetaProps(self, r, mmmObject):
        maName = mmmObject.intention["Name"] + ".Has.MetaProperty"
        for mmmMetaProperty in mmmObject.eachOutgoingClass(maName):
            mmmMetaType = mmmMetaProperty.firstOutgoingClass("MetaProperty.Has.MetaType")
            r.MetaProperty(mmmObject["Name"], 
                           mmmMetaProperty["Name"], 
                           mmmMetaType["Name"], 
                           mmmMetaProperty["Description"])
      
    def extract(self, r):
        # Create from MetaModel, MetaClass, MetaAssoc objects -> Classes in SpiderMMM
        rMMM = self._createMetaModel(Watcher())        
        
        # First create a meta model node
        mmmMetaModel = rMMM.Class("MetaModel")
        mmmMetaModel["Name"] = r["Name"]
        mmmMetaModel["Description"] = r["Description"]
        mmmMetaModel["Type"] = r["Type"]
        mmmMetaModel["FileName"] = r["FileName"]
        mmmMetaModel["UUID"] = r["UUID"]
        mmmMetaModel["Icon"] = r["Icon"]
        mmmMetaModel["MajorVersion"] = r["MajorVersion"]
        mmmMetaModel["MinorVersion"] = r["MinorVersion"]

        # Second recreate meta type and meta enum nodes
        for mt in r.eachMetaType():
            mmmMetaType = rMMM.Class("MetaType")
            mmmMetaType["Name"] = mt["Name"]
            mmmMetaType["Description"] = mt["Description"]
            mmmMetaType["DefaultValue"] = mt["DefaultValue"]
            rMMM.Assoc(mmmMetaModel, mmmMetaType, "Contains")

            if mt.isMetaEnum():
                mmmMetaEnum = rMMM.Class("MetaEnum")
                mmmMetaEnum["Name"] = mt["Name"]
                mmmMetaEnum["Description"] = mt["Description"]
                rMMM.Assoc(mmmMetaType, mmmMetaEnum, "Is")
                for mat in mt.eachMetaAtom():
                    mmmMetaAtom = rMMM.Class("MetaAtom")
                    mmmMetaAtom["Name"] = mat["Name"]
                    mmmMetaAtom["Description"] = mat["Description"]
                    rMMM.Assoc(mmmMetaEnum, mmmMetaAtom, "HasPart")
                
        # Third create meta class nodes and meta properties
        for mc in r.eachMetaClass():
            mmmMetaClass = rMMM.Class("MetaClass")
            mmmMetaClass["Name"] = mc["Name"]
            mmmMetaClass["Description"] = mc["Description"]
            rMMM.Assoc(mmmMetaModel, mmmMetaClass, "Contains")
            self._extractMetaProps(rMMM, mmmMetaClass, mc)

        # Forth create meta assoc nodes and meta properties
        for ma in r.eachMetaAssoc():
            mmmMetaAssoc = rMMM.Class("MetaAssoc")
            mmmMetaAssoc["Name"] = ma["Name"]
            mmmMetaAssoc["Description"] = ma["Description"]
            mmmMetaAssoc["SourceName"] = ma["SourceName"]
            mmmMetaAssoc["TargetName"] = ma["TargetName"]
            mmmMetaAssoc["AssocName"] = ma["AssocName"]
            mmmMetaAssoc["SourceCardinality"] = ma["SourceCardinality"]
            mmmMetaAssoc["TargetCardinality"] = ma["TargetCardinality"]
            mmmMetaAssoc["Aggregation"] = ma["Aggregation"]
            rMMM.Assoc(mmmMetaModel, mmmMetaAssoc, "Contains")
            self._extractMetaProps(rMMM, mmmMetaAssoc, mc)

            mmmSourceMetaClass = rMMM.find("MetaClass", "Name", ma["SourceName"])
            assert(mmmSourceMetaClass)
            mmmTargetMetaClass = rMMM.find("MetaClass", "Name", ma["TargetName"])
            assert(mmmTargetMetaClass)
            rMMM.Assoc(mmmMetaAssoc, mmmSourceMetaClass, "Source")
            rMMM.Assoc(mmmMetaAssoc, mmmTargetMetaClass, "Target")
        return rMMM

    def _extractMetaProps(self, rMMM, mmmMetaEntity, mo):
        for mp in mo.eachMetaProperty():
            mt = mp.metaType
            mmmMetaType = rMMM.find("MetaType", "Name", mt["Name"])
            assert(mmmMetaType)
            mmmMetaProperty = rMMM.Class("MetaProperty")
            mmmMetaProperty["Name"] = mp["Name"]
            mmmMetaProperty["Description"] = mp["Description"]
            rMMM.Assoc(mmmMetaProperty, mmmMetaType, "Has")
            rMMM.Assoc(mmmMetaEntity, mmmMetaProperty, "Has")


class Application(Component):
  def __init__(self):
     pass

  def interface(self, interfaceName):
     return None
     

"""
ini = IniFile()
r = ini.repository
for i in range(0,20000):
  sc = r.Class("Section")
  st = r.Class("Setting")
  r.Assoc(sc, st, "Has")
"""
